package distribution

import (
	"context"
	"errors"
	"fmt"
	"hotgo/internal/consts"
	"hotgo/internal/dao"
	"hotgo/internal/library/contexts"
	"hotgo/internal/library/dict"
	"hotgo/internal/library/hfsm"
	"hotgo/internal/library/hgorm/handler"
	"hotgo/internal/model"
	"hotgo/internal/model/entity"
	"hotgo/internal/model/input/adminin"
	"hotgo/internal/model/input/sysin"
	"hotgo/internal/service"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gctx"
	"github.com/shopspring/decimal"
)

type sDistribution struct {
}

func NewDistribution() *sDistribution {
	return &sDistribution{}
}

func init() {
	service.RegisterDistribution(NewDistribution())
}

func (s *sDistribution) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
	return handler.Model(dao.Distribution.Ctx(ctx), option...)
}

// 利润分配
type ProfitDistribution struct {
	// 分销配置信息
	Config *model.DistributionConfig
	// 订单
	Order entity.Order
	// 商品
	Product entity.Product
	// 分销记录
	Distribution entity.Distribution
	// 利润
	ProfitAmount decimal.Decimal
	// 会员分销金额
	VipAmount decimal.Decimal
	// 商品绑定销氪分销金额
	SalesKrBindProductAmount decimal.Decimal
	// 用户绑定销氪分销金额
	SalesKrBindUserAmount decimal.Decimal
}

// 商品分销
func (s *sDistribution) Product(ctx context.Context, orderUID string) {
	// 创建分销记录
	if err := service.SysDistribution().Edit(ctx, &sysin.DistributionEditInp{
		Distribution: entity.Distribution{
			TraceId:  gctx.CtxId(ctx),
			OrderUid: orderUID,
			Status:   consts.DistributionStatusCreate,
			Type:     consts.DistributionTypeProduct,
		},
	}); err != nil {
		g.Log().Errorf(ctx, "创建分销记录失败 err: %v", err)
		return
	}
	hfsm.DFSM.NewFSM(dict.GetOptionLabel(consts.DistributionStatusOptions, consts.DistributionStatusCreate), gctx.CtxId(ctx))
	_ = hfsm.DFSM.PushState(ctx, gctx.CtxId(ctx), consts.DistributionEventProcessing)

	return
}

func (s *sDistribution) ProductDo(ctx context.Context, traceID string) (err error) {

	// 利润分销
	pd := &ProfitDistribution{}

	// 获取分销记录
	if err = dao.Distribution.Ctx(ctx).Where(dao.Distribution.Columns().TraceId, traceID).Scan(&pd.Distribution); err != nil {
		g.Log().Errorf(ctx, "获取分销记录失败 err: %v", err)
		return
	}
	// 获取订单信息
	if err = dao.Order.Ctx(ctx).Where(dao.Order.Columns().Uid, pd.Distribution.OrderUid).Scan(&pd.Order); err != nil {
		g.Log().Errorf(ctx, "获取订单信息失败")
		return
	}
	// 获取产品信息
	if err = dao.Product.Ctx(ctx).WherePri(pd.Order.ProductId).Scan(&pd.Product); err != nil {
		g.Log().Errorf(ctx, "获取产品信息失败")
		return
	}
	// 利润配置信息
	pd.Config, err = service.SysConfig().GetDistribution(ctx)
	if err != nil {
		g.Log().Errorf(ctx, "获取分销配置失败 err: %v", err)
		return
	}
	// 利润分销
	if err = s.profitDistribution(ctx, pd); err != nil {
		g.Log().Errorf(ctx, "销氪分销失败 err: %v", err)
		return
	}
	return nil
}

// 会员分销
func (s *sDistribution) vipDistribution(ctx context.Context, pd *ProfitDistribution) (err error) {
	// 是否是会员
	var shareMember entity.AdminMember
	if err = dao.AdminMember.Ctx(ctx).WherePri(pd.Order.ShareMemberId).Scan(&shareMember); err != nil {
		g.Log().Errorf(ctx, "获取会员信息失败 err: %v", err)
		return
	}
	if shareMember.MemberStatus == consts.AdminMemberMemberStatusMember {
		// 手续费
		fee := pd.VipAmount.Mul(decimal.NewFromInt(int64(pd.Config.DistributionSalesKrFee)).Div(decimal.NewFromInt(100)))
		// 保留2位小数
		pd.VipAmount = pd.VipAmount.Sub(fee).Truncate(2)
		// 更新我的余额
		err = s.addBalanceOperation(ctx, addBalanceInp{
			MemberId:     pd.Order.ShareMemberId,
			Num:          pd.VipAmount.InexactFloat64(),
			OrderID:      pd.Order.Id,
			Remark:       fmt.Sprintf("订单【%s】分销", pd.Order.Uid),
			Distribution: pd.Distribution,
			ProductId:    pd.Product.Id,
			Proportion:   pd.Product.VipProportion,
			Type:         consts.DistributionVipShare,
		})
		if err != nil {
			g.Log().Errorf(ctx, "会员分销失败 err: %v", err)
			return
		}

		// 剩余利润 = 利润 - 会员分销金额
		pd.ProfitAmount = pd.ProfitAmount.Sub(pd.VipAmount)
	}
	return
}

// 销氪分销(产品绑定，用户绑定)
func (s *sDistribution) salesKrDistribution(ctx context.Context, pd *ProfitDistribution, distributionType int) (err error) {
	var (
		memberId      int64                 // 查询的用户ID
		pMembers      *[]entity.AdminMember // 上级用户链
		xiaokeMember  *entity.AdminMember   // 销氪
		serviceMember *entity.AdminMember   // 服务商
		fee           decimal.Decimal       // 销氪分销金额手续费
		feeFee        decimal.Decimal       // 销氪分销金额手续费 的 手续费
		amount        decimal.Decimal       // 分销金额
		proportion    int                   // 分销比例
	)

	switch distributionType {
	case consts.DistributionProductSaleKr: // 商品绑定销氪分销
		amount = pd.SalesKrBindProductAmount
		proportion = pd.Config.DistributionSalesKrBindProduct
		memberId = pd.Product.AgentId
	case consts.DistributionUserBindSaleKr: // 用户绑定销氪分销
		amount = pd.SalesKrBindUserAmount
		proportion = pd.Config.DistributionSalesKrBindUser
		memberId = pd.Order.UserId
	default:
		return errors.New("未知的分销类型")
	}

	// 商品绑定销氪分销
	pMembers, err = service.AdminMember().GetIdentityChain(ctx, memberId, consts.AdminMemberIdentityXiaoke)
	if err != nil {
		g.Log().Errorf(ctx, "获取商品绑定销氪分销上级用户链条失败 err: %v", err)
		return err
	}
	if len(*pMembers) >= 1 {
		xiaokeMember = &(*pMembers)[0]
	}
	if len(*pMembers) >= 2 {
		serviceMember = &(*pMembers)[1]
	}

	if xiaokeMember != nil {
		// 分销手续费计算
		fee = amount.Mul(decimal.NewFromInt(int64(pd.Config.DistributionSalesKrFee)).Div(decimal.NewFromInt(100)))
		// 剩余分销金额 = 总金额 - 手续费
		amount = amount.Sub(fee)

		// 保留2位小数
		amount = amount.Truncate(2)

		// 销氪分销金额
		err = s.addBalanceOperation(ctx, addBalanceInp{
			MemberId:     xiaokeMember.Id,
			Num:          amount.InexactFloat64(),
			OrderID:      pd.Order.Id,
			Remark:       fmt.Sprintf("订单【%s】分销", pd.Order.Uid),
			Distribution: pd.Distribution,
			ProductId:    pd.Product.Id,
			Proportion:   proportion,
			Type:         distributionType,
		})
		if err != nil {
			g.Log().Errorf(ctx, "销氪分销失败 err: %v", err)
			return
		}

		// 减去分销金额
		pd.ProfitAmount = pd.ProfitAmount.Sub(amount)

		if serviceMember != nil {
			// 有上级服务商，设置手续费的手续费
			feeFee = fee.Mul(decimal.NewFromInt(int64(pd.Config.DistributionSalesKrFee)).Div(decimal.NewFromInt(100)))
			// 手续费 = 手续费 - 手续费的手续费
			fee = fee.Sub(feeFee)

			// 保留2位小数
			fee = fee.Truncate(2)

			err = s.addBalanceOperation(ctx, addBalanceInp{
				MemberId:     serviceMember.Id,
				Num:          fee.InexactFloat64(),
				OrderID:      pd.Order.Id,
				Remark:       fmt.Sprintf("订单【%s】销氪【%s】分销", pd.Order.Uid, xiaokeMember.Nickname),
				Distribution: pd.Distribution,
				ProductId:    pd.Product.Id,
				Proportion:   pd.Config.DistributionSalesKrFee,
				Type:         consts.DistributionFee,
			})
			if err != nil {
				g.Log().Errorf(ctx, "分销手续费失败 err: %v", err)
				return
			}

			// 减去手续费
			pd.ProfitAmount = pd.ProfitAmount.Sub(fee)
		}
	}
	return nil
}

// 产品分销
func (s *sDistribution) profitDistribution(ctx context.Context, pd *ProfitDistribution) (err error) {

	// 利润 = 销售价 - 成本价
	pd.ProfitAmount = decimal.NewFromFloat(pd.Order.UseAmount).Sub(decimal.NewFromFloat(pd.Order.CostPrice))
	// 会员分销金额 = 利润 * 会员分销比例
	pd.VipAmount = pd.ProfitAmount.Mul(decimal.NewFromInt(int64(pd.Product.VipProportion)).Div(decimal.NewFromInt(100)))

	// 会员分享分销
	if pd.Product.VipProportion > 0 && pd.Order.ShareMemberId > 0 { // 有设置会员分销比例，并且有分享用户
		err = s.vipDistribution(ctx, pd)
		if err != nil {
			g.Log().Errorf(ctx, "会员分销失败 err: %v", err)
			return err
		}
	}

	// 利润
	if pd.ProfitAmount.LessThanOrEqual(decimal.NewFromInt(0)) {
		g.Log().Errorf(ctx, "分销金额小于等于0")
		return
	}

	// 商品绑定销氪分销金额 = 剩余利润 * 销氪绑定商品分销比例
	pd.SalesKrBindProductAmount = decimal.NewFromFloat(pd.Order.UseAmount).Sub(decimal.NewFromFloat(pd.Order.CostPrice)).
		Mul(decimal.NewFromInt(100).Sub(decimal.NewFromInt(int64(pd.Product.VipProportion))).Div(decimal.NewFromInt(100))).
		Mul(decimal.NewFromInt(int64(pd.Config.DistributionSalesKrBindProduct)).Div(decimal.NewFromInt(100)))

	// 用户绑定销氪分销金额 = 剩余利润 * 销氪绑定用户分销比例
	pd.SalesKrBindUserAmount = decimal.NewFromFloat(pd.Order.UseAmount).Sub(decimal.NewFromFloat(pd.Order.CostPrice)).
		Mul(decimal.NewFromInt(100).Sub(decimal.NewFromInt(int64(pd.Product.VipProportion))).Div(decimal.NewFromInt(100))).
		Mul(decimal.NewFromInt(int64(pd.Config.DistributionSalesKrBindUser)).Div(decimal.NewFromInt(100)))

	// 产品绑定销氪分销
	if pd.Product.AgentId > 0 { // 有绑定销氪
		err = s.salesKrDistribution(ctx, pd, consts.DistributionProductSaleKr)
		if err != nil {
			g.Log().Errorf(ctx, "产品绑定销氪分销失败 err: %v", err)
			return
		}
	}

	// 用户绑定销氪分销
	if pd.Order.UserId > 0 { // 有绑定用户
		err = s.salesKrDistribution(ctx, pd, consts.DistributionUserBindSaleKr)
		if err != nil {
			g.Log().Errorf(ctx, "用户绑定销氪分销失败 err: %v", err)
			return
		}
	}

	// 公司分销金额
	err = s.addBalanceOperation(ctx, addBalanceInp{
		MemberId:     1, // 超级管理员
		Num:          pd.ProfitAmount.InexactFloat64(),
		OrderID:      pd.Order.Id,
		Remark:       fmt.Sprintf("订单【%s】分销", pd.Order.Uid),
		Distribution: pd.Distribution,
		ProductId:    pd.Product.Id,
		Proportion:   pd.Config.DistributionCompanyProportion,
		Type:         consts.DistributionToCompany,
	})
	if err != nil {
		g.Log().Errorf(ctx, "会员分销失败 err: %v", err)
		return
	}

	return nil
}

// 会员分销
func (s *sDistribution) PromoPro(ctx context.Context, orderUID string) (err error) {
	// 创建分销记录
	if err = service.SysDistribution().Edit(ctx, &sysin.DistributionEditInp{
		Distribution: entity.Distribution{
			TraceId:  gctx.CtxId(ctx),
			OrderUid: orderUID,
			Status:   consts.DistributionStatusCreate,
			Type:     consts.DistributionTypeVip,
		},
	}); err != nil {
		g.Log().Errorf(ctx, "创建分销记录失败 err: %v", err)
		return
	}
	hfsm.DFSM.NewFSM(dict.GetOptionLabel(consts.DistributionStatusOptions, consts.DistributionStatusCreate), gctx.CtxId(ctx))
	_ = hfsm.DFSM.PushState(ctx, gctx.CtxId(ctx), consts.DistributionEventProcessing)
	return nil
}

func (s *sDistribution) PromoProDo(ctx context.Context, traceID string) (err error) {
	// 获取分销记录
	var distribution entity.Distribution
	if err = dao.Distribution.Ctx(ctx).Where(dao.Distribution.Columns().TraceId, traceID).Scan(&distribution); err != nil {
		g.Log().Errorf(ctx, "获取分销记录失败 err: %v", err)
		return
	}
	// 获取订单信息
	var order entity.Order
	if err = dao.Order.Ctx(ctx).Where(dao.Order.Columns().Uid, distribution.OrderUid).Scan(&order); err != nil {
		g.Log().Errorf(ctx, "获取订单信息失败")
		return
	}
	// 获取会员卡信息
	var memberCard entity.MemberCard
	if err = dao.MemberCard.Ctx(ctx).WherePri(order.MemberCardId).Scan(&memberCard); err != nil {
		g.Log().Errorf(ctx, "获取会员卡信息失败 err: %v", err)
		return
	}
	// 下单人信息
	var orderMember entity.AdminMember
	if err = dao.AdminMember.Ctx(ctx).WherePri(order.UserId).Scan(&orderMember); err != nil {
		g.Log().Errorf(ctx, "获取下单人信息失败 err: %v", err)
		return
	}

	// 最小身份等级
	minIdentity := consts.AdminMemberIdentityMember
	if memberCard.Type > 2 {
		minIdentity = consts.AdminMemberIdentityXiaoke
	}

	// 分销类型
	distributionType := 0
	switch memberCard.Type {
	case consts.AdminMemberIdentityMember:
		distributionType = consts.DistributionBuyVip
	case consts.AdminMemberIdentityXiaoke:
		distributionType = consts.DistributionBuySaleKr
	case consts.AdminMemberIdentityService:
		distributionType = consts.DistributionBuyService
	}

	// 获取上级信息
	pMembers, err := service.AdminMember().GetIdentityChain(ctx, orderMember.Id, minIdentity)
	if err != nil {
		g.Log().Errorf(ctx, "获取上级信息失败 err: %v", err)
		return
	}
	// 利润配置信息
	config, err := service.SysConfig().GetDistribution(ctx)
	if err != nil {
		g.Log().Errorf(ctx, "获取分销配置失败 err: %v", err)
		return
	}

	// 总金额
	totalAmount := decimal.NewFromFloat(memberCard.Price)

	// 会员分销金额
	memberAmount := decimal.NewFromFloat(memberCard.MemberProfit)
	// 销氪分销金额
	xiaokeAmount := decimal.NewFromFloat(memberCard.SaleProfit).Sub(memberAmount)
	// 销氪手续费金额
	xiaokeAmountFee := decimal.NewFromFloat(0)
	// 服务商分销金额
	serviceAmount := decimal.NewFromFloat(memberCard.ServiceProfit).Sub(xiaokeAmount).Sub(memberAmount)

	for _, pMember := range *pMembers {
		// 是否有会员
		switch consts.GetIdentity(pMember) {
		case consts.AdminMemberIdentityService: // 服务商分销
			amount := memberAmount.Add(xiaokeAmount).Add(serviceAmount).Add(xiaokeAmountFee).
				Mul(decimal.NewFromInt(100).Sub(decimal.NewFromInt(int64(config.DistributionSalesKrFee))).Div(decimal.NewFromInt(100)))
			amount = amount.Truncate(2)
			err = s.addBalanceOperation(ctx, addBalanceInp{
				MemberId:     pMember.Id,
				Num:          amount.InexactFloat64(),
				OrderID:      order.Id,
				Remark:       fmt.Sprintf("订单【%s】分销", order.Uid),
				Distribution: distribution,
				Type:         distributionType,
			})
			if err != nil {
				g.Log().Errorf(ctx, "服务商分销失败 err: %v", err)
				return
			}
			// 剩余金额
			memberAmount = decimal.NewFromFloat(0)
			xiaokeAmount = decimal.NewFromFloat(0)
			serviceAmount = decimal.NewFromFloat(0)
			totalAmount = totalAmount.Sub(amount)
		case consts.AdminMemberIdentityXiaoke: // 销氪分销
			xiaokeAmountFee = memberAmount.Add(xiaokeAmount).Mul(decimal.NewFromInt(int64(config.DistributionSalesKrFee)).Div(decimal.NewFromInt(100)))
			amount := memberAmount.Add(xiaokeAmount).Sub(xiaokeAmountFee)
			amount = amount.Truncate(2)
			err = s.addBalanceOperation(ctx, addBalanceInp{
				MemberId:     pMember.Id,
				Num:          amount.InexactFloat64(),
				OrderID:      order.Id,
				Remark:       fmt.Sprintf("订单【%s】分销", order.Uid),
				Distribution: distribution,
				Type:         distributionType,
			})
			if err != nil {
				g.Log().Errorf(ctx, "销氪分销失败 err: %v", err)
				return
			}
			// 剩余金额
			memberAmount = decimal.NewFromFloat(0)
			xiaokeAmount = decimal.NewFromFloat(0)
			totalAmount = totalAmount.Sub(amount)
		case consts.AdminMemberIdentityMember: // 会员分销
			// 分销金额
			amount := memberAmount.Mul(decimal.NewFromInt(100).Sub(decimal.NewFromInt(int64(config.DistributionSalesKrFee))).Div(decimal.NewFromInt(100)))
			amount = amount.Truncate(2)
			err = s.addBalanceOperation(ctx, addBalanceInp{
				MemberId:     pMember.Id,
				Num:          amount.InexactFloat64(),
				OrderID:      order.Id,
				Remark:       fmt.Sprintf("订单【%s】分销", order.Uid),
				Distribution: distribution,
				Type:         distributionType,
			})
			if err != nil {
				g.Log().Errorf(ctx, "会员分销失败 err: %v", err)
				return
			}
			// 剩余金额
			memberAmount = decimal.NewFromFloat(0)
			totalAmount = totalAmount.Sub(amount)
		}
	}

	// 公司分销
	err = s.addBalanceOperation(ctx, addBalanceInp{
		MemberId:     1, // 超级管理员
		Num:          totalAmount.InexactFloat64(),
		OrderID:      order.Id,
		Remark:       fmt.Sprintf("订单【%s】分销", order.Uid),
		Distribution: distribution,
		Type:         consts.DistributionToCompany,
	})
	if err != nil {
		g.Log().Errorf(ctx, "服务商分销失败 err: %v", err)
		return
	}

	return nil
}

// 添加余额
type addBalanceInp struct {
	MemberId     int64               // 用户ID
	Num          float64             // 金额
	OrderID      int64               // 订单ID
	Remark       string              // 备注
	Distribution entity.Distribution // 分销信息
	ProductId    int64               // 商品ID
	Proportion   int                 // 分销比例
	Type         int                 // 分销类型
}

// 处理余额
func (s *sDistribution) addBalanceOperation(ctx context.Context, in addBalanceInp) (err error) {
	if err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) {
		res, err := service.AdminCreditsLog().SaveBalance(ctx, &adminin.CreditsLogSaveBalanceInp{
			MemberId:    in.MemberId,
			Num:         in.Num,
			OrderID:     in.OrderID,
			Remark:      in.Remark,
			AppId:       consts.AppDefault,
			AddonsName:  contexts.GetModule(ctx),
			CreditGroup: consts.CreditGroupDistributionIncr,
		})
		if err != nil {
			return err
		}
		if err = service.SysDistributionDetail().Edit(ctx, &sysin.DistributionDetailEditInp{
			DistributionDetail: entity.DistributionDetail{
				TraceId:      in.Distribution.TraceId,
				Mid:          in.Distribution.Id,
				MemberId:     in.MemberId,
				ProductId:    in.ProductId,
				CreditsLogId: res.Id,
				Amount:       in.Num,
				Proportion:   in.Proportion,
				Type:         in.Type,
			},
		}); err != nil {
			return err
		}
		return
	}); err != nil {
		return err
	}
	return nil
}
